/******************************************************************************/
/*! 
    filename
    [2012]

    \file		
    \namespace    
    \brief      
*/
/******************************************************************************/
/*** Includes *****************************************************************/
#include "NMemoryManager.h"
#include "NCoreException.h"

#include <iostream>
#include <fstream>
#include <time.h>
#include <assert.h>

/*** Using ********************************************************************/
using namespace Memory;

/*** Macros *******************************************************************/

/*** Constants ****************************************************************/
MemoryManager* MemoryManager::sInstance = NULL;
#define TIME_BUFFER_SIZE 80

/*** Declarations *************************************************************/
int GetRangedRandom(int min, int max);
void GetFileNameFromMacro(const char* source, char* dest);

/*** Variables ****************************************************************/
bool G_IsMemoryManagerActive;	

/*** Implementation ***********************************************************/
/******************************************************************************/
/*! [Heap] :: [Heap]

    \brief  
    \param  name - name of the Heap
	\param	size - size of the Heap
    \return -void
*/
/******************************************************************************/
MemoryManager::Heap::Heap(const char* name, int size)
	: mInitialSize(size),
	  mSize(size),
	  mFirstFreeBlock(NULL),
	  mHeapStart(NULL),
	  mNumberOfBlocks(0)
{
	SetName(name);
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
MemoryManager::Heap::~Heap(void)
{
	if(mHeapStart != NULL)
	{
		free(mHeapStart);
		mHeapStart = NULL;
		mInitialSize = 0;
		mSize = 0;
	}
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void MemoryManager::Heap::Init(const char*name , int size)
{
	//
	mSignature = HashName(name);
	mSize = size - GetBlockOverHead();
	mInitialSize = size;
	SetName(name);
	mNumberOfBlocks = 0;
	
	//
	mHeapStart = malloc(size);
	if(mHeapStart != NULL)
	{
		memset(mHeapStart, 0, size);			//set memory of heap to zero
	}
	else
	{
		throw std::exception("");
	}

	//
	mFirstFreeBlock = (AllocHeader*) mHeapStart;
	if(mFirstFreeBlock != NULL)
	{
		mFirstFreeBlock->Size = mSize;
	}
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void MemoryManager::Heap::SetName(const char* name)
{
	int length = std::strlen(name);
	if(length > NAME_LENGTH)
	{
		throw std::exception("MemoryManager::Heap::SetName - Name Too Long");
	}
	for(int i = 0; i < NAME_LENGTH ; i++)
	{
		mName[i] = name[i];
	}
}
/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
const char* MemoryManager::Heap::GetName() const
{
	return mName;
}

unsigned int MemoryManager::Heap::GetSize() const
{
	return mInitialSize;
}

unsigned int MemoryManager::Heap::UsedSize() const
{
	return mInitialSize - mSize;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
int MemoryManager::Heap::GetSignature() const
{
	return mSignature;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void* MemoryManager::Heap::AllocateBlock(size_t size,char * file, double line, const char* tag)
{
	unsigned int actualBlockSize = size + GetFrontBlockOverHead();

	if(mHeapStart == NULL)
	{
		throw std::exception("MemoryManager::Heap::AllocateBlock - Attempting To Allocate On A Null Heap");
	}

	if(actualBlockSize > GetLargestFreeBlock())
	{
		if(actualBlockSize > mSize)
		{
			throw std::exception("MemoryManager::Heap::AllocateBlock - Heap Is Too Small");
		}
		else
		{
			throw std::exception("MemoryManager::Heap::AllocateBlock - Heap Is Full");
		}
	}

	AllocHeader* newBlock = NULL;
	bool blockFound = false;

	AllocHeader* currentBlock = mFirstFreeBlock;
	AllocHeader* oldPrev = NULL;
	AllocHeader* oldNext = NULL;

	while(!blockFound)
	{
		if(currentBlock->Used == false && (int)actualBlockSize < currentBlock->Size)
		{
			blockFound = true;
			newBlock = currentBlock;

			oldPrev = currentBlock->Previous;
			oldNext = currentBlock->Next;
			//move
			int tempsize = currentBlock->Size;
			if(currentBlock == mFirstFreeBlock)
			{
				currentBlock = (AllocHeader*)((int) currentBlock + actualBlockSize);
				currentBlock->Previous = newBlock;
				currentBlock->Next = oldNext;
				currentBlock->Size = tempsize - actualBlockSize;
				currentBlock->Used = false;
				mFirstFreeBlock = currentBlock;
				mFirstFreeBlock->Signature = mSignature;
			}
			else
			{
				currentBlock = (AllocHeader*)((int) currentBlock + actualBlockSize);
				currentBlock->Previous = newBlock;
				currentBlock->Next = oldNext;
				currentBlock->Size = tempsize - actualBlockSize;
				currentBlock->Signature = mSignature;
				currentBlock->Used = false;
			}

			

			//Set up new block
			newBlock->Size = size;
			newBlock->Signature = mSignature;			
			newBlock->Used = true;

			newBlock->AllocInfo.File = file;
			newBlock->AllocInfo.Line = line;
			newBlock->AllocInfo.Tag = tag;

			//link block
			newBlock->Next = currentBlock;
			newBlock->Previous = oldPrev;
			if(oldPrev != NULL)
			{
				oldPrev->Next = newBlock;
			}
			if(oldNext != NULL)
			{
				oldNext->Previous = currentBlock;
			}
		}
	
		if(!blockFound)
		{
			if(currentBlock->Next == NULL)
			{
				DumpHeapToFile("..\\..\\memoryDumps\\errorDump.txt");
				throw std::exception("MemoryManager::Heap::AllocateBlock - Block was not found while attempting to allocate memory");
			}
			currentBlock = currentBlock->Next;
		}
	}
	
	mSize -= GetBlockSize(newBlock);
	mNumberOfBlocks++;
	CheckHeapConsistency();
	return (void*)((int) newBlock +  GetFrontBlockOverHead());;
}
/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void MemoryManager::Heap::FreeBlock(void* memory)
{
	AllocHeader* block = (AllocHeader*)((int) FindBeginningOfMemoryBlock(memory));

	if(block == NULL)
	{
		throw std::exception("MemoryManager::Heap::FreeBlock - Memory Not Found");
	}
	if(mNumberOfBlocks == 0)
	{
		throw std::exception("MemoryManager::Heap::FreeBlock - Number of blocks = 0");
	}
	if(block->Used == false)
	{
		throw std::exception("MemoryManager::Heap::FreeBlock - Double Free");
	}

	//mark block as free
	block->Used = false;
	mNumberOfBlocks--;
	mSize += GetBlockSize(block);

	//combine free block
	if(block->Previous != NULL && block->Previous->Used == false)
	{
		if(!CombineBlocks(block->Previous,block))
		{
			throw std::exception("MemoryManager::Heap::FreeBlock - CombineBlocks Error");
		}
		block = block->Previous;
	}
	if(block->Next != NULL && block->Next->Used== false)
	{
		if(!CombineBlocks(block,block->Next))
		{
			throw std::exception("MemoryManager::Heap::FreeBlock - CombineBlocks Error");
		}
	}

	if((int)block < (int)mFirstFreeBlock)
	{
		mFirstFreeBlock = block;
	}

	//check consistency of heap
	CheckHeapConsistency();

	if(mNumberOfBlocks == 0 && mFirstFreeBlock->Size != mInitialSize)
	{
		CombineBlocks(mFirstFreeBlock,mFirstFreeBlock->Next);
	}
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
unsigned int MemoryManager::Heap::FreeSpace() const
{
	unsigned int freeSpace = 0;
	
	AllocHeader* block = (AllocHeader*)mHeapStart;
	while(block != NULL)
	{
		if(block->Used == false)
		{
			freeSpace += block->Size;
		}
		block = block->Next;
	}

	return freeSpace;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void* MemoryManager::Heap::FindBeginningOfMemoryBlock(void* ptr)
{
	AllocHeader* thisHeap = (AllocHeader*)mHeapStart;
	while(thisHeap != NULL)
	{
		int blockMemory = (int)thisHeap;
		blockMemory += sizeof(AllocHeader);

		int ptrv = (int)ptr;
		if(blockMemory == ptrv )
		{
			return thisHeap;
		}
		thisHeap = (AllocHeader*)thisHeap->Next;
	}
	return NULL;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
unsigned int MemoryManager::Heap::SpaceBetweenBlocks(AllocHeader* block1, AllocHeader* block2)
{
	if( block1 == NULL || block2 == NULL)
	{
		return 0;
	}

	int memLocation1 = (int)block1 + block1->Size + GetFrontBlockOverHead();
	int memLocation2 = (int)block2;
	unsigned int sizeBetween = memLocation2 - memLocation1;

	return sizeBetween;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
int MemoryManager::Heap::GetBlockSize(const AllocHeader* block) const
{
	int size = 0;
	if(block != NULL)
	{
		size += GetFrontBlockOverHead();
		size += block->Size;
	}
	return size;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
int MemoryManager::Heap::DistanceToEndOfMemory(const AllocHeader* block) const
{
	size_t memAddress = (size_t)block;
	memAddress += block->Size  + sizeof(AllocHeader);

	size_t endAddress = (size_t)mHeapStart;
	endAddress += mSize;

	return endAddress - memAddress;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void* MemoryManager::Heap::EndOfHeader(const AllocHeader* block) const
{
	int iMem = (int)block;
	int memLoc = iMem + block->Size + GetFrontBlockOverHead();
	return (void*)memLoc;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
int MemoryManager::Heap::HashName(const char* name)
{
	int length = std::strlen(name);
	int retValue = 0;
	for(int i = 0; i < length;i++)
	{
		retValue ^= (retValue << 2) + (retValue >> 1) + name[i];
	}
	return retValue;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
unsigned int MemoryManager::Heap::GetNumFreeBlocks()
{
	int numFreeBlocks = 0;
	AllocHeader* currentBlock = (AllocHeader*)mHeapStart;
	while(currentBlock)
	{
		if(currentBlock->Used == false)
		{
			numFreeBlocks++;
		}
		currentBlock = currentBlock->Next;
	}
	return numFreeBlocks;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
unsigned int MemoryManager::Heap::GetTotalFreeMemory()
{
	int totalFreeMemory = 0;
	AllocHeader* currentBlock = (AllocHeader*)mHeapStart;
	while(currentBlock)
	{
		if(currentBlock->Used == false)
		{
			totalFreeMemory += GetBlockSize(currentBlock);
		}
		currentBlock = currentBlock->Next;
	}
	return totalFreeMemory;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
unsigned int MemoryManager::Heap::GetLargestFreeBlock()
{
	int largetFreeBlock = 0;
	AllocHeader* currentFreeBlockBlock = (AllocHeader*)mHeapStart;
	while(currentFreeBlockBlock)
	{
		if(currentFreeBlockBlock->Used == false 
			&& GetBlockSize(currentFreeBlockBlock) > largetFreeBlock)
		{
			largetFreeBlock = GetBlockSize(currentFreeBlockBlock) ;
		}
		currentFreeBlockBlock = currentFreeBlockBlock->Next;
	}
	return largetFreeBlock;
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  Check to make sure the heap list is correctly linked properly
    \param  
    \return
*/
/******************************************************************************/
bool MemoryManager::Heap::CheckHeapConsistency()
{
	AllocHeader* currentHeader = (AllocHeader*)mHeapStart;
	int count = 0;
	AllocHeader*  prev = NULL;

	while(currentHeader->Next != NULL)
	{
		if(currentHeader->Used)
		{
			count++;
		}

		assert(prev == currentHeader->Previous);
		prev = currentHeader;
		currentHeader = currentHeader->Next;
	}

	assert(count == mNumberOfBlocks);
	return true;
}
/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
bool MemoryManager::Heap::CombineBlocks(AllocHeader* b1, AllocHeader* b2)
{
	bool nextNULL = false;
	if(b1 == NULL || b2 == NULL)
	{
		return false;
	}
	if(b2->Previous != b1 || b1->Next != b2)
	{
		throw std::exception("MemoryManager::Heap::CombineBlocks - Combining wrong blocks");
	}
	if( b2->Next == NULL)
	{
		nextNULL = true;
	}
	b1->Next = b2->Next;
	if(b2->Next != NULL)
	{
		b2->Next->Previous = b1;
	}
	
	b1->Size += GetBlockSize(b2);
	CheckHeapConsistency();	
	return true;
}
/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void MemoryManager::Heap::DumpHeapToFile(const char* fileName) const
{
	std::ofstream file;
	file.open(fileName, std::ios_base::app);

	if(file.is_open())
	{
		AllocHeader* memory = (AllocHeader*)mHeapStart;

		file << "Heap Name:";
		file << mName;

		file << "\tHeap Size:";
		file << mInitialSize;

		file << "\tSpace Used:";
		int spaceRemaining = mInitialSize - mSize;
		file << spaceRemaining;

		file  << "\n";
		file  << "------------------------------------------------------------\n";

		while(memory != NULL)
		{
			if(memory->Used)
			{
				file  << "Tag:";
				file  << memory->AllocInfo.Tag;
				file  << "\t\t";

				file << "Size:";
				file << memory->Size;
				file << "||";
				file <<  GetBlockSize(memory);
				file << "\t";

				file  << "File:";
				char fileName[FILENAME_MAX];
				GetFileNameFromMacro(memory->AllocInfo.File,fileName);
				file  << fileName;
				file  << "\t";

				file  << "LineNumber:";
				file  << memory->AllocInfo.Line;
				file  << "\n";	
			}
			else
			{
				file  << "Tag:";
				file  << "Free";
				file  << "\t\t";

				file << "Size:";
				file << memory->Size;
				file << "||";
				file <<  GetBlockSize(memory);
				file  << "\n";
			}
			memory = memory->Next;
		}
		file  << "\n";
	}

	file.close();
}
/******************************************************************************/
/*! Attributed :: [Function]

    \brief  

    \param  
    \return
*/
/******************************************************************************/
void MemoryManager::Heap::UnitTest()
{/*
	
	//	Tests: AllocateBlockation and freeing of a block of memory
	
	char *string1 = (char *)AllocateBlock(13,__FILE__,__LINE__,"Unit Test"); //Allocatate a block of memory of size 13 bytes 

	char *string2 = (char *)AllocateBlock(22,__FILE__,__LINE__,"Unit Test"); //Allocatate a block of memory of size 22 bytes 

    strcpy_s(string1,13,"Hello World!");

	strcpy_s(string2,22,"Hello World!");

    FreeBlock(string1);

    FreeBlock(string2);
	
	//	Tests: Allocatation and freeing of many blocks of memory
	
	char *randomNumbers = (char *)AllocateBlock(100,__FILE__,__LINE__,"Unit Test");
    char *randomNumbers1 = randomNumbers;
    for (int i=0;i<100;++i)
    {
        *randomNumbers++ = (char)22;//getRangedRandom(0,255);
    }
    FreeBlock(randomNumbers1);
    

	
	//	Tests: AllocateBlockating a size of memory is correctly taken from the heap
	
    char *tempBlock1,*tempBlock2,*tempBlock3;
    CheckHeapConsistency();
    unsigned int size = GetBlockSize(mFirstFreeBlock);//mFirstFreeBlockBlock?
    tempBlock1 = (char *)AllocateBlock(500,__FILE__,__LINE__,"Unit Test");
    CheckHeapConsistency();
    size = GetBlockSize(mFirstFreeBlock);
    NASSERT(size < GetSize()-500);  //some overhead anyway...
    NASSERT(GetNumberOfBlocks() == 1);

	
	//	Tests: AllocateBlockating and freeing a size of memory is correctly taken from the heap 
	
    tempBlock2 = (char *)AllocateBlock(500,__FILE__,__LINE__,"Unit Test");
    CheckHeapConsistency();
    size = GetBlockSize(mFirstFreeBlock);
    NASSERT(size < GetSize()-1000);  //some overhead anyway...
    NASSERT(GetNumberOfBlocks() == 2);

    FreeBlock(tempBlock2);
    CheckHeapConsistency();
    NASSERT(GetNumberOfBlocks() == 1);

    FreeBlock(tempBlock1);
    CheckHeapConsistency();
    NASSERT(GetNumberOfBlocks() == 0);

	
	//	Tests: Combining free blocks of memory
	
    tempBlock1 = (char *)AllocateBlock(500,__FILE__,__LINE__,"Unit Test");
    tempBlock2 = (char *)AllocateBlock(500,__FILE__,__LINE__,"Unit Test");
    tempBlock3 = (char *)AllocateBlock(500,__FILE__,__LINE__,"Unit Test");
    NASSERT(GetNumberOfBlocks() == 3);

    FreeBlock(tempBlock1);
    CheckHeapConsistency();
    NASSERT(GetNumberOfBlocks() == 2);
    FreeBlock(tempBlock3);
    CheckHeapConsistency();
    NASSERT(GetNumberOfBlocks() == 1);
    FreeBlock(tempBlock2);
    CheckHeapConsistency();
    NASSERT(GetNumberOfBlocks() == 0);

	
	//	Tests: Consistency when creating fragmentation and combination of memory
	
    int freeMemory = GetLargestFreeBlock();
    NASSERT(GetSize()== freeMemory);

    tempBlock1 = (char *)AllocateBlock(500,__FILE__,__LINE__,"Unit Test");
    freeMemory = GetLargestFreeBlock();
    NASSERT(GetSize()-(500+GetBlockOverHead()) == freeMemory);

    tempBlock2 = (char *)AllocateBlock(500,__FILE__,__LINE__,"Unit Test");
    tempBlock3 = (char *)AllocateBlock(500,__FILE__,__LINE__,"Unit Test");
    freeMemory = GetLargestFreeBlock();
    NASSERT(GetSize()-(1500+(3*GetBlockOverHead())) == freeMemory);

    FreeBlock(tempBlock1);
    freeMemory = GetLargestFreeBlock();
    NASSERT(GetSize()-(1500+(3*GetBlockOverHead())) == freeMemory);
    freeMemory = GetTotalFreeMemory();

	int value = GetSize()-(1000+(2*GetBlockOverHead()));
    NASSERT(GetSize()-(1000+(2*GetBlockOverHead())) == freeMemory);
    NASSERT(GetNumFreeBlocks() == 2);

    FreeBlock(tempBlock2);
    freeMemory = GetLargestFreeBlock();
    NASSERT(GetSize()-(1500+(3*GetBlockOverHead())) == freeMemory);
    freeMemory = GetTotalFreeMemory();
    NASSERT(GetSize()-(500+(GetBlockOverHead())) == freeMemory);
    NASSERT(GetNumFreeBlocks() == 2);

    FreeBlock(tempBlock3);
    freeMemory = GetLargestFreeBlock();
    NASSERT(GetSize() == freeMemory);
    freeMemory = GetTotalFreeMemory();
    NASSERT(GetSize() == freeMemory);
    NASSERT(GetNumFreeBlocks() == 1);

#ifdef _DEBUG
    char *tempBlock4,*tempBlock5,*tempBlock6;

	
	//	Test: Dumping Heaps to file
	
    tempBlock1 = (char *)AllocateBlock(500,__FILE__,__LINE__,"unit test 1");
    tempBlock2 = (char *)AllocateBlock(500,__FILE__,__LINE__,"unit test 2");
    tempBlock3 = (char *)AllocateBlock(500,__FILE__,__LINE__,"unit test 3");
    tempBlock4 = (char *)AllocateBlock(500,__FILE__,__LINE__,"unit test 4");
    tempBlock5 = (char *)AllocateBlock(500,__FILE__,__LINE__,"unit test 5");
    tempBlock6 = (char *)AllocateBlock(500,__FILE__,__LINE__,"unit test 6");
    NASSERT(GetNumFreeBlocks() == 1);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump0.txt");
    
    FreeBlock(tempBlock2);
    FreeBlock(tempBlock3);
    tempBlock2 = tempBlock3 = NULL;
    NASSERT(GetNumFreeBlocks() == 2);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump1.txt");

    tempBlock2 = (char *)AllocateBlock(800,__FILE__,__LINE__,"unit test 7");
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump1a.txt");
    FreeBlock(tempBlock4);
    FreeBlock(tempBlock5);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump1b.txt");
    tempBlock4 = tempBlock5 = NULL;

    tempBlock3 = (char *)AllocateBlock(950,__FILE__,__LINE__,"unit test 8");
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump2.txt");
    FreeBlock(tempBlock1);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump2a.txt");
    FreeBlock(tempBlock6);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump3.txt");
    FreeBlock(tempBlock2);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump4.txt");
    FreeBlock(tempBlock3);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump5.txt");

    NASSERT(GetSize() == freeMemory);
    //NASSERT(GetNumFreeBlocks() == 2);



    tempBlock1 = (char *)AllocateBlock(500,__FILE__,__LINE__,"unit test 1");
    tempBlock2 = (char *)AllocateBlock(500,__FILE__,__LINE__,"unit test 2");
    tempBlock3 = (char *)AllocateBlock(500,__FILE__,__LINE__,"unit test 3");
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump1.txt");
    
    FreeBlock(tempBlock2);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump2.txt");
    FreeBlock(tempBlock1);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump3.txt");
    FreeBlock(tempBlock3);
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump4.txt");

//AllocateBlockate a thousand pointers, then deAllocateBlock them
    char *tempPtrs[1000];
    for(int i=0;i<1000;++i)
    {
        tempPtrs[i] = (char *)AllocateBlock(GetRangedRandom(32,500),__FILE__,__LINE__,"random AllocateBlock");
    }
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump5.txt");
    for(int i=0;i<1000;++i)
    {
        int blockID = GetRangedRandom(0,999);
        if(tempPtrs[blockID]!= NULL)
        {
            FreeBlock(tempPtrs[blockID]);
            tempPtrs[blockID]=NULL;
        }
    }
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump6.txt");
    for(int i=0;i<1000;++i)
    {
        if(tempPtrs[i]!= NULL)
        {
            FreeBlock(tempPtrs[i]);
            tempPtrs[i]=NULL;
        }
    }
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump7.txt");

    for(int i=0;i<10000;++i)
    {
        int blockID = GetRangedRandom(0,999);

        if(tempPtrs[blockID]!= NULL)
        {
            FreeBlock(tempPtrs[blockID]);
            tempPtrs[blockID]=NULL;
        }
        else
        {
            tempPtrs[blockID] = (char *)AllocateBlock(GetRangedRandom(32,5000),__FILE__,__LINE__,"AllocateBlock");
        }
    }
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump8.txt");
    for(int i=0;i<1000;++i)
    {
        if(tempPtrs[i]!= NULL)
        {
            FreeBlock(tempPtrs[i]);
            tempPtrs[i]=NULL;
        }
    }
    DumpHeapToFile("..\\..\\memoryDumps\\UnitTestDump9.txt");
#endif*/
}

/******************************************************************************/
/*! MemoryManager :: MemoryManager

    \brief  
    \param  
    \return
*/
/******************************************************************************/
MemoryManager::MemoryManager(void)
	:mMaxNumberOfHeap(0),
	 mCurrentNumberOfHeaps(0)
{
	mHeapList = (Heap*) malloc(NUM_OF_HEAPS * sizeof(Heap));
	mMaxNumberOfHeap = NUM_OF_HEAPS;

	CreateHeap("Default",	HEAP_SIZE);
	CreateHeap("Game",		HEAP_SIZE);
	CreateHeap("Engine",	HEAP_SIZE);
	CreateHeap("Dynamic",	HEAP_SIZE);
	CreateHeap("Graphics",  HEAP_SIZE);
	//CreateHeap("Physics", HEAP_SIZE*100);

	G_IsMemoryManagerActive = true;
}
/******************************************************************************/
/*! MemoryManager:: ~MemoryManager

    \brief  
    \param  
    \return
*/
/******************************************************************************/
MemoryManager::~MemoryManager(void)
{
	free(mHeapList);
	G_IsMemoryManagerActive = false;
}
/******************************************************************************/
/*! MemoryManager :: Initialize

    \brief  
    \param  
    \return
*/
/******************************************************************************/
void MemoryManager::Initialize()
{
	if(sInstance == NULL)
	{
		char* buffer = (char*)malloc(sizeof(MemoryManager));
		sInstance = new(buffer) MemoryManager();
		G_IsMemoryManagerActive = true;
	}
}

/******************************************************************************/
/*! MemoryManager :: Deinitialize

    \brief  
    \param  
    \return
*/
/******************************************************************************/
void MemoryManager::Deinitialize()
{	
	if(sInstance != NULL)
	{
		DUMP_MEMORY
		sInstance->~MemoryManager();
		free (sInstance);
		sInstance = NULL;
	}
}

/******************************************************************************/
/*! MemoryManager :: GetInstance

    \brief  
    \param  
    \return
*/
/******************************************************************************/
MemoryManager* MemoryManager::GetInstance()
{
	return sInstance;
}

/******************************************************************************/
/*! MemoryManager :: CreateHeap

    \brief  
    \param  heapName - name of the heap
	\param  heapSize - size of the heap
    \return -void
*/
/******************************************************************************/
void MemoryManager::CreateHeap(const char* heapName, int heapSize)
{
	assert(heapName != NULL);
	assert(heapSize != 0);
	assert(!(mCurrentNumberOfHeaps > mMaxNumberOfHeap - 1));
	
	mHeapList[mCurrentNumberOfHeaps++].Init(heapName,heapSize);
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  
    \param  
    \return
*/
/******************************************************************************/
MemoryManager::Heap* MemoryManager::GetHeap(int heapNumber)
{
	return &mHeapList[heapNumber];
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  
    \param  
    \return
*/
/******************************************************************************/
void* MemoryManager::Allocate(int size, HID heapId, char * file, double line, const char* tag)
{
	try
	{
		if(!G_IsMemoryManagerActive)
		{
			return malloc(size);
		}
		else if(heapId > HID_Count)
		{
			throw std::exception("MemoryManager::Allocate - Heap Id Out of Range");
		}
		return mHeapList[heapId].AllocateBlock(size,file,line,tag);
	}
	catch(std::exception e)
	{
		DumpAllHeaps();
		throw std::exception(e);
	}
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  
    \param  
    \return
*/
/******************************************************************************/
void MemoryManager::Free(void* memory)
{
	if(!G_IsMemoryManagerActive)
	{
		free(memory);
	}
	else
	{
		try
		{ 
			//assert(memory != NULL);
			if(memory != NULL)
			{
				AllocHeader* header = NULL;
				header = (AllocHeader*)((int) memory - Heap::GetFrontBlockOverHead());
				Heap* addr = GetHeapBySig(header->Signature);

				if(addr == NULL )
				{
					free(memory);
				}
				else
				{
					addr->FreeBlock(memory);
				}
			}
		}
		catch(std::exception e)
		{
			DumpAllHeaps();
		}
	}
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  
    \param  
    \return
*/
/******************************************************************************/
void MemoryManager::DumpAllHeaps()
{
	time_t rawtime;
	struct tm timeinfo;
	char fileName [TIME_BUFFER_SIZE];
 
	time ( &rawtime );
	errno_t error = localtime_s (&timeinfo,&rawtime);
	if(error)
	{
		throw std::exception("MemoryManager::DumpAllHeaps - Time Fail");
	}

#ifdef MULIT_DUMP
	strftime (fileName,TIME_BUFFER_SIZE,"..\\..\\bin\\MemoryManagerDump_%b%d_%H%M.log",&timeinfo);
#else
	strftime (fileName,TIME_BUFFER_SIZE,"..\\..\\bin\\MemoryManagerDump.log",&timeinfo);
#endif
	
	puts (fileName);

	for(int i = 0; i < mCurrentNumberOfHeaps;i++)
	{
		mHeapList[i].DumpHeapToFile(fileName);
	}
}

/******************************************************************************/
/*! Attributed :: [Function]

    \brief  
    \param  
    \return
*/
/******************************************************************************/
MemoryManager::Heap* MemoryManager::GetHeapBySig(int sig)
{
	for(int i = 0; i < mCurrentNumberOfHeaps;i++)
	{
		if(mHeapList[i].GetSignature() == sig)
		{
			return &mHeapList[i];
		}
	}
	return NULL;
}

unsigned int MemoryManager::GetMaxMemory()
{
	unsigned int size = 0;
	for(int i = 0; i < mCurrentNumberOfHeaps;i++)
	{
		size += mHeapList[i].GetSize();
	}

	return size;
}

unsigned int MemoryManager::GetUsedMemory()
{
	unsigned int size = 0;
	for(int i = 0; i < mCurrentNumberOfHeaps;i++)
	{
		size +=  mHeapList[i].UsedSize();
	}

	return size;
}

void MemoryManager::Enable()
{
	G_IsMemoryManagerActive = true;
}

void MemoryManager::Disable()
{
	G_IsMemoryManagerActive = false;
}

/*

*/
void* operator new (size_t size, Memory::HID heapId, char * file, double line, const char* tag)
{
	if(Memory::MemoryManager::GetInstance())
	{
		return Memory::MemoryManager::GetInstance()->Allocate(size, heapId, file, line, tag);
	}
	return malloc(size);
}

void *operator new[](size_t size)
{
	/*if(Memory::MemoryManager::GetInstance())
	{
		return Memory::MemoryManager::GetInstance()->Allocate(size, (Memory::HID) 0,__FILE__, __LINE__, "Default New Array Allocation");
	}*/
	return malloc(size);
}

void* operator new (size_t size)
{
	/*if(Memory::MemoryManager::GetInstance())
	{
		return Memory::MemoryManager::GetInstance()->Allocate(size, (Memory::HID) 0, __FILE__, __LINE__, "Default New Allocation");
	}*/
	return malloc(size);
}

void *operator new[](size_t size, Memory::HID heapId, char * file, double line, const char* tag)
{
	if( Memory::MemoryManager::GetInstance())
	{
		return Memory::MemoryManager::GetInstance()->Allocate(size, heapId, file, line, tag);
	}
	return malloc(size);
}

void operator delete(void *mem)
{
	if( Memory::MemoryManager::GetInstance())
	{
		Memory::MemoryManager::GetInstance()->Free(mem);
	}
	else
	{
		free(mem);
	}
}

void operator delete[](void *mem)
{
	if(Memory::MemoryManager::GetInstance())
	{
		Memory::MemoryManager::GetInstance()->Free(mem);
	}
	else
	{
		free(mem);
	}
}

void operator delete(void* mem, Memory::HID , char * , double , const char* )
{
	if( Memory::MemoryManager::GetInstance())
	{
		Memory::MemoryManager::GetInstance()->Free(mem);
	}
	else
	{
		free(mem);
	}
}

void operator delete[](void* mem,  Memory::HID , char * , double , const char* )
{
	if( Memory::MemoryManager::GetInstance())
	{
		Memory::MemoryManager::GetInstance()->Free(mem);
	}
	else
	{
		free(mem);
	}
}

/******************************************************************************/
/*! FileOpen

	\brief 

	\param  
	\return 
*/
/******************************************************************************/
void FileOpen(FILE* filePtr,const char* fileName)
{
	if(fileName != NULL)
	{
		fopen_s(&filePtr, fileName, "rb");
	}
}
/******************************************************************************/
/*! FileClose

	\brief  Ctor

	\param  -void
	\return -void
*/
/******************************************************************************/
void FileClose(FILE * file)
{
	if(file != NULL)
	{
		fclose(file);
	}
}
/******************************************************************************/
/*! FileLoad

	\brief  Ctor

	\param  -void
	\return -void
*/
/******************************************************************************/
void FileLoad(char ** buffer , const char* fileName)
{
	if(!strlen(fileName))
	{
		throw std::exception("NMemoryManager::FileLoad::Invalid Parameters");
	}

	FILE * thefile = NULL;
	FileOpen(thefile, fileName);
		
	if(buffer == NULL || thefile == NULL)
	{
		throw std::exception("NMemoryManager::FileLoad::Invalid Parameters");
	}
		
	fseek(thefile, 0, SEEK_END);
	long lSize = ftell(thefile);
	rewind(thefile);

	*buffer = MY_NEW(3,"FileLoad::") char[lSize+1];

	fread (*buffer,1,lSize,thefile);

	(*buffer)[lSize] = NULL;

	FileClose(thefile);
}
/******************************************************************************/
/*! GetFileNameFromMacro

	\brief  Ctor

	\param  -void
	\return -void
*/
/******************************************************************************/
void GetFileNameFromMacro(const char* source, char* dest)
{
	int j = 0;
	char character;
	for(unsigned int i = 0; i < strlen(source);i++)
	{
		character = source[i];
		if(character == '\\')
		{
			dest[0] = NULL;
			j = 0;
		}
		else
		{
			dest[j] = character;
			j++;
		}
	}
	dest[j] = 0;
}
/******************************************************************************/
/*! GetRangedRandom

	\brief  Ctor

	\param  -void
	\return -void
*/
/******************************************************************************/
int GetRangedRandom(int min, int max)
{
	return rand() % max + min;
}